package com.fujimana.rockincat.restree.proxy;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.regex.Pattern;

import com.fujimana.rockincat.restree.annotation.rest.Param;
import com.fujimana.rockincat.restree.context.Context;
import com.fujimana.rockincat.restree.util.ClassUtils;

public class ResourceProxy {

	private ResourceMetaData metaData;

	private Class<?> target;

	private Object instance;

	ResourceProxy(ResourceMetaData metaData) {
		this.target = metaData.getTarget();
		this.metaData = metaData;
	}

	public void handle(Context context) {
		if (instance == null) {
			this.instance = ClassUtils.newInstance(this.target);
		}

		Method method = this.metaData.getHandleMethod();

		if (method != null) {
			ClassUtils.invokeMethod(instance, method, new Object[] { context });
		}
	}

	public Class<?> getParamBodyType(Class<? extends Annotation> method) {
		BindMetaData bindMetaData = metaData.getBindMetaData(method);

		if (bindMetaData != null) {
			return bindMetaData.getTarget();
		}

		return null;
	}

	public String getParamBodyName(Class<? extends Annotation> method) {
		BindMetaData bindMetaData = metaData.getBindMetaData(method);

		if (bindMetaData != null) {
			return bindMetaData.getName();
		}

		return null;
	}

	public Object call(Class<? extends Annotation> method, Context rstCtx) {
		if (instance == null) {
			this.instance = ClassUtils.newInstance(this.target);
		}

		Method m = this.metaData.getInvokeMethod(method);

		Object[] args = parseArgumet(m, rstCtx);

		return ClassUtils.invokeMethod(instance, m, args);
	}

	private static Object[] parseArgumet(Method method, Context ctx) {
		Object[] ret = new Object[method.getParameterTypes().length];

		Annotation[][] annos = method.getParameterAnnotations();

		for (int i = 0; i < ret.length; i++) {
			Object set = null;
			for (Annotation a : annos[i]) {

				if (Param.class.getName().equals(a.annotationType().getName())) {
					set = ctx.get(((Param) a).value());
					break;
				}
			}

			ret[i] = set;
		}

		return ret;
	}

	public boolean hasInvokeMethod(Class<? extends Annotation> method) {
		Method m = this.metaData.getInvokeMethod(method);

		return m != null;
	}

	public ResourceProxy getResource(String res,
			Map<String, ResourceMetaData> metaDatas) {

		Class<?>[] child = this.metaData.getChildResource();

		if (child == null) {
			return null;
		}

		for (Class<?> clazz : child) {
			ResourceMetaData metaData = metaDatas.get(clazz.getName());

			if (metaData == null) {
				continue;
			}

			if (metaData.getPath().equals(res)) {
				return ResourceProxyFactory.getInstance().getProxy(metaData);
			}
		}

		for (Class<?> clazz : child) {
			ResourceMetaData metaData = metaDatas.get(clazz.getName());

			if (metaData == null) {
				continue;
			}

			if (Pattern.compile(metaData.getPath()).matcher(res).find()) {
				return ResourceProxyFactory.getInstance().getProxy(metaData);
			}
		}

		return null;
	}

}
